package com.dlc.felear.xiaoensale.server;


import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;

import com.dlc.felear.xiaoensale.constant.Constant;
import com.dlc.felear.xiaoensale.helper.BlueToochHelper;
import com.dlc.felear.xiaoensale.helper.JsonHelper;
import com.dlc.felear.xiaoensale.utils.HexUtil;
import com.felhr.usbserial.CDCSerialDevice;
import com.felhr.usbserial.UsbSerialDevice;
import com.felhr.usbserial.UsbSerialInterface;

import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;

public class UsbService extends Service {

    public static final String TAG = "UsbService";

    public static final String ACTION_USB_READY = "com.felhr.connectivityservices.USB_READY";
    public static final String ACTION_USB_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
    public static final String ACTION_USB_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED";
    public static final String ACTION_USB_NOT_SUPPORTED = "com.felhr.usbservice.USB_NOT_SUPPORTED";
    public static final String ACTION_NO_USB = "com.felhr.usbservice.NO_USB";
    public static final String ACTION_USB_PERMISSION_GRANTED = "com.felhr.usbservice.USB_PERMISSION_GRANTED";
    public static final String ACTION_USB_PERMISSION_NOT_GRANTED = "com.felhr.usbservice.USB_PERMISSION_NOT_GRANTED";
    public static final String ACTION_USB_DISCONNECTED = "com.felhr.usbservice.USB_DISCONNECTED";
    public static final String ACTION_CDC_DRIVER_NOT_WORKING = "com.felhr.connectivityservices.ACTION_CDC_DRIVER_NOT_WORKING";
    public static final String ACTION_USB_DEVICE_NOT_WORKING = "com.felhr.connectivityservices.ACTION_USB_DEVICE_NOT_WORKING";
    public static final int MESSAGE_FROM_SERIAL_PORT = 0;
    public static final int CTS_CHANGE = 1;
    public static final int DSR_CHANGE = 2;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private static final int BAUD_RATE = 115200;
    public static boolean SERVICE_CONNECTED = false;

    private IBinder binder = new UsbBinder();

    private Context context;
    private Handler mHandler;
    private UsbManager usbManager;
    private UsbDevice device;
    private UsbDeviceConnection connection;
    private UsbSerialDevice serialPort;

    private int productId = 60000;
    private int vendorId = 4292;
    private static final String ERROR="ERROR";

    private boolean serialPortConnected;
    private static ArrayBlockingQueue<byte[]> mQueue = new ArrayBlockingQueue<>(40);
    private byte[] tempBytArr = null;
    private String lastCmd = "";
    private HashMap<String, String> cmdMap = new HashMap<>();
    private HashMap<String, byte[]> cmdByt = new HashMap<>();
    private HashMap<String, Integer> cmdTimes = new HashMap<>();

    private UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
        @Override
        public void onReceivedData(byte[] arg0) {
            try {
                Log.e(TAG, "onReceivedData:" + HexUtil.bytes2HexString(arg0));
                String data = new String(arg0, "UTF-8").trim();
                if(TextUtils.equals(data,"DEVICE START")){
                    write("+++".getBytes(StandardCharsets.UTF_8));
                    Log.e(TAG,"重新插拔蓝牙串口模块,切换蓝牙模式");
                    xianshiMsg("重新插拔蓝牙串口模块,切换蓝牙模式");
                    return;
                }
                reviceBleData(arg0);
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage());
            }
        }
    };

    private void reviceBleData(byte[] arg0) {
        try {
            String tempData = HexUtil.bytes2HexString(arg0);
            Log.e(TAG, "reviceBleData:" + tempData);
            if (tempData.endsWith("0D 0A")) {
                Log.e(TAG, "onReceivedData 完整数据");
                if (tempBytArr == null) {
                    sendData(arg0);
                } else {
                    sendData(pinjieshuzu(tempBytArr, arg0));
                    tempBytArr = null;
                }
            } else {
                Log.e(TAG, "onReceivedData 不完整数据");
                tempBytArr = arg0;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private byte[] pinjieshuzu(byte[] arr1, byte[] arr2) {
        int l1 = arr1.length;
        int l2 = arr2.length;
        Log.e(TAG, "pinjieshuzu arr1:" + HexUtil.bytes2HexString(arr1));
        Log.e(TAG, "pinjieshuzu arr2:" + HexUtil.bytes2HexString(arr2));
        byte[] data = new byte[l1 + l2];
        for (int i = 0; i < l1 + l2; i++) {
            if (i < l1) {
                data[i] = arr1[i];
            } else {
                data[i] = arr2[i - l1];
            }
            Log.e(TAG, "pinjieshuzu data:" + HexUtil.bytes2HexString(data));
        }
        Log.e(TAG, "pinjieshuzu 拼接完整的数组:" + HexUtil.bytes2HexString(data));
        return data;
    }

    private void sendData(byte[] arg0) {
        try {
            tempBytArr = null;
            String data = new String(arg0, "UTF-8").trim();
            Log.e(TAG,"sendData: data00:"+data);
            if (TextUtils.equals("OK", data) || TextUtils.equals(ERROR, data)) {
                Log.e(TAG,"sendData: data11:"+data);
                cmdMap.put(lastCmd, data);
                if (TextUtils.equals(ERROR, data)) {
//                    reSendCmd();
                }
            }
            Log.e(TAG, "sendData onReceivedData:" + data);
            EventBus.getDefault().post(data.trim(), Constant.EVENTBUS_TAG_R_LY);
            if (mHandler != null)
                mHandler.obtainMessage(MESSAGE_FROM_SERIAL_PORT, data).sendToTarget();
        } catch (Exception e) {

        }
    }

    private void reSendCmd() {
        try {
            Log.e(TAG,"reSendCmd1:"+JsonHelper.toJson(cmdMap));
            for (String key : cmdMap.keySet()) {
                if(TextUtils.isEmpty(key)){
                    cmdMap.remove(key);
                    continue;
                }
                String status=cmdMap.get(key);
                int times = cmdTimes.get(key);
                if (times >= 0&&TextUtils.equals(status,ERROR)) {
                    mQueue.put(cmdByt.get(key));
                    times--;
                    cmdTimes.put(key, times);
                    Log.e(TAG,"reSendCmd2:"+new String(cmdByt.get(key), "UTF-8"));
                }else {
                    cmdMap.remove(key);
                }
            }
        } catch (Exception e) {

        }
    }

    /*
     * State changes in the CTS line will be received here
     */
    private UsbSerialInterface.UsbCTSCallback ctsCallback = new UsbSerialInterface.UsbCTSCallback() {
        @Override
        public void onCTSChanged(boolean state) {
            if (mHandler != null)
                mHandler.obtainMessage(CTS_CHANGE).sendToTarget();
        }
    };

    /*
     * State changes in the DSR line will be received here
     */
    private UsbSerialInterface.UsbDSRCallback dsrCallback = new UsbSerialInterface.UsbDSRCallback() {
        @Override
        public void onDSRChanged(boolean state) {
            if (mHandler != null)
                mHandler.obtainMessage(DSR_CHANGE).sendToTarget();
        }
    };
    /*
     * Different notifications from OS will be received here (USB attached, detached, permission responses...)
     * About BroadcastReceiver: http://developer.android.com/reference/android/content/BroadcastReceiver.html
     */
    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent arg1) {
//            Log.e(TAG,"onReceive:"+ JsonHelper.toJson(arg1));
            if (arg1.getAction().equals(ACTION_USB_PERMISSION)) {
                boolean granted = arg1.getExtras().getBoolean(UsbManager.EXTRA_PERMISSION_GRANTED);
                if (granted) // User accepted our USB connection. Try to open the device as a serial port
                {
                    Intent intent = new Intent(ACTION_USB_PERMISSION_GRANTED);
                    arg0.sendBroadcast(intent);
                    Log.e(TAG, "onReceive:connection:" + JsonHelper.toJson(device));
                    connection = usbManager.openDevice(device);
                    new ConnectionThread().start();
                } else // User not accepted our USB connection. Send an Intent to the Main Activity
                {
                    Intent intent = new Intent(ACTION_USB_PERMISSION_NOT_GRANTED);
                    arg0.sendBroadcast(intent);
                }
            } else if (arg1.getAction().equals(ACTION_USB_ATTACHED)) {
                if (!serialPortConnected)
                    findSerialPortDevice(); // A USB device has been attached. Try to open it as a Serial port
            } else if (arg1.getAction().equals(ACTION_USB_DETACHED)) {
                // Usb device was disconnected. send an intent to the Main Activity
                xianshiMsg("外置蓝牙模块连接断开");
                Intent intent = new Intent(ACTION_USB_DISCONNECTED);
                arg0.sendBroadcast(intent);
                if (serialPortConnected) {
                    serialPort.close();
                }
                serialPortConnected = false;
            }
        }
    };

    @Override
    public void onCreate() {
        this.context = this;
        EventBus.getDefault().register(this);
        serialPortConnected = false;
        UsbService.SERVICE_CONNECTED = true;
        setFilter();
        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        findSerialPortDevice();
        loop();
        Log.e(TAG, "UsbService onCreate");
    }

    public void loop() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] s = null;
                    while ((s = mQueue.take()) != null) {
                        write(s);
                        Thread.sleep(1000);
                    }
                } catch (Exception e) {
                }
            }
        }).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        serialPort.close();
        unregisterReceiver(usbReceiver);
        UsbService.SERVICE_CONNECTED = false;
    }

    /*
     * This function will be called from MainActivity to write data through Serial Port
     */
    public void write(byte[] data) {
        Log.e(TAG, "serialPort:" + serialPort);
        try {
            if (serialPort != null) {
                serialPort.write(data);
                lastCmd = new String(data, "UTF-8");
                cmdMap.put(lastCmd,ERROR);
            } else {
                xianshiMsg("外置蓝牙模块连接断开");
            }
        }catch (Exception e){

        }
    }

    public void setHandler(Handler mHandler) {
        this.mHandler = mHandler;
    }

    private void findSerialPortDevice() {
        // This snippet will try to open the first encountered usb device connected, excluding usb root hubs
        HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
        if (!usbDevices.isEmpty()) {
            for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
                device = entry.getValue();
                int deviceVID = device.getVendorId();
                int devicePID = device.getProductId();
                Log.e(TAG, "deviceVID:" + deviceVID + " devicePID:" + devicePID);
                if (isSupported(device)) {
                    requestUserPermission();
                    break;
                } else {
                    connection = null;
                    device = null;
                }
            }
            if (device == null) {
                // There are no USB devices connected (but usb host were listed). Send an intent to MainActivity.
                Intent intent = new Intent(ACTION_NO_USB);
                sendBroadcast(intent);
            }
        } else {
            Log.d(TAG, "findSerialPortDevice() usbManager returned empty device list.");
            // There is no USB devices connected. Send an intent to MainActivity
            Intent intent = new Intent(ACTION_NO_USB);
            sendBroadcast(intent);
        }
    }


    private boolean isSupported(UsbDevice device) {
        if (device.getProductId() == productId && device.getVendorId() == vendorId) {
            return true;
        }
        return false;
    }

    private void setFilter() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_USB_PERMISSION);
        filter.addAction(ACTION_USB_DETACHED);
        filter.addAction(ACTION_USB_ATTACHED);
        registerReceiver(usbReceiver, filter);
    }


    private void requestUserPermission() {
        Log.d(TAG, String.format("requestUserPermission(%X:%X)", device.getVendorId(), device.getProductId()));
        PendingIntent mPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        usbManager.requestPermission(device, mPendingIntent);
    }

    public class UsbBinder extends Binder {
        public UsbService getService() {
            return UsbService.this;
        }
    }


    private class ConnectionThread extends Thread {
        @Override
        public void run() {
            serialPort = UsbSerialDevice.createUsbSerialDevice(device, connection);
            if (serialPort != null) {
                if (serialPort.open()) {
                    serialPortConnected = true;
                    serialPort.setBaudRate(BAUD_RATE);
                    serialPort.setDataBits(UsbSerialInterface.DATA_BITS_8);
                    serialPort.setStopBits(UsbSerialInterface.STOP_BITS_1);
                    serialPort.setParity(UsbSerialInterface.PARITY_NONE);
                    serialPort.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
                    serialPort.setDTR(true);
                    serialPort.setRTS(true);
                    serialPort.read(mCallback);
                    serialPort.getCTS(ctsCallback);
                    serialPort.getDSR(dsrCallback);
                    Intent intent = new Intent(ACTION_USB_READY);
                    context.sendBroadcast(intent);
                    EventBus.getDefault().post(ACTION_USB_READY, "USB_LY_INFO");
                    write("+++".getBytes(StandardCharsets.UTF_8));
                    xianshiMsg("外置蓝牙模块连接成功");
                } else {
                    if (serialPort instanceof CDCSerialDevice) {
                        Intent intent = new Intent(ACTION_CDC_DRIVER_NOT_WORKING);
                        EventBus.getDefault().post(ACTION_CDC_DRIVER_NOT_WORKING, "USB_LY_INFO");
                        context.sendBroadcast(intent);
                    } else {
                        EventBus.getDefault().post(ACTION_USB_DEVICE_NOT_WORKING, "USB_LY_INFO");
                        Intent intent = new Intent(ACTION_USB_DEVICE_NOT_WORKING);
                        context.sendBroadcast(intent);
                    }
                }
            } else {
                // No driver for given device, even generic CDC driver could not be loaded
                Intent intent = new Intent(ACTION_USB_NOT_SUPPORTED);
                context.sendBroadcast(intent);
                EventBus.getDefault().post(ACTION_USB_NOT_SUPPORTED, "USB_LY_INFO");
            }
        }
    }

    @Subscriber(tag = Constant.EVENT_RECEIVE_INPU_IO11)
    public void receiverCmdData(byte[] orders) {
        try {
            mQueue.put(orders);
            String data = new String(orders, "UTF-8");
            Log.e(TAG, "发送指令:" + data);
            if(TextUtils.equals(BlueToochHelper.AT_SCAN_CMD,data)){
                cmdMap.put(data, "ERROR");
                cmdByt.put(data, orders);
                cmdTimes.put(data, 5);
            }
        } catch (Exception e) {
        }
    }


    private List<byte[]> testArr(byte[] data) {
        int l1 = data.length - data.length / 2;
//        Log.e(TAG,"testArr data:"+HexUtil.bytes2HexString(data));
//        Log.e(TAG,"l1:"+l1+" l2:"+l2+" l:"+data.length);
        List<byte[]> lis = new ArrayList<>();
        byte[] data1 = new byte[l1];
        byte[] data2 = new byte[data.length - l1];
        for (int i = 0; i < data.length; i++) {
            if (i < l1) {
                data1[i] = data[i];
            } else {
                data2[i - l1] = data[i];
            }
        }
        lis.add(data1);
        lis.add(data2);
//        Log.e(TAG,"testArr data1:"+HexUtil.bytes2HexString(data1));
//        Log.e(TAG,"testArr data2:"+HexUtil.bytes2HexString(data2));
        return lis;
    }

    private void xianshiMsg(String msg) {
        EventBus.getDefault().post(msg, Constant.SERIALMSG);
        Log.e(TAG,msg);
    }

}
